#include <sys/select.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

/**
 * select例程
 * 由于select只能用于终端设备和网络设备, 而PC最常见的终端设备就是键盘和鼠标
 * 键盘对应标准输入设备stdin
 * 鼠标文件路径/dev/input/mouse?, ?具体是0, 1, 还是几, 需要实际测试
 * pselect和select差别在于最后一个参数(超时时间)类型不同, pselect是struct timespec类型的,
 * select是struct timeval的, 而struct timespec表示时间可以精确到1ns, timeval是1us. 
 * 实际时间精度取决于系统时钟
 */
int main() {
    int ret;
    char buf[100];
    int  mousepos;

    int mousefd = open("/dev/input/mouse0", O_RDONLY);
    if (mousefd < 0) {
        perror("open mouse error");    
        exit(1);
    }

    fd_set readfds;
    struct timeval timeover;

    while (1) {
        // select可能修改最后一个参数(超时时间)的值, 所以不能期望其值不变
        // 这是因为select可能中断返回, 超时时间会被修改为剩余时间
        timeover.tv_sec = 2;
        timeover.tv_usec = 0;

        FD_ZERO(&readfds);
        FD_SET(STDIN_FILENO, &readfds);
        FD_SET(mousefd, &readfds);
        
        do {
            ret = select(mousefd + 1, &readfds, NULL, NULL, &timeover);
        } while (ret < 0 && errno == EINTR);

        if (ret < 0 && errno != EINTR) {
                perror("select error");
                exit(1);
        }
        else if (ret > 0) {
            if (FD_ISSET(STDIN_FILENO, &readfds)) { // 标准输入设备对应fd置位, 也就是有数据
                memset(buf, 0, sizeof(buf));
                int readret = read(STDIN_FILENO, buf, sizeof(buf));
                if (readret > 0) printf("%s\n", buf);
            }

            if (FD_ISSET(mousefd, &readfds)) {
                mousepos = 0;
                int readret = read(mousefd, &mousepos, sizeof(mousepos));
                if (readret > 0) printf("%d\n", mousepos);
            }
        }
        else if (ret == 0) { // select 超时
            printf("time out\n");
        }

    }

    close(mousefd);

    return 0;
}


